#include "caffe/caffe.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <algorithm>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <sstream>
#include "classification.hpp"

using namespace caffe;
using namespace std;
using namespace cv;

class Classifier;
Classifier::Classifier() {
  Caffe::set_mode(Caffe::GPU);//设置caffe工作模式为CPU/GPU
  string model_file   = "../lib/deploy.prototxt";//网络定义文件
  string trained_file = "../lib/hand.caffemodel";//训练得到的网络参数参数文件
  net_.reset(new Net<float>(model_file, TEST));//设置网络为测试模式，只进行前向传播
  net_->CopyTrainedLayersFrom(trained_file);//导入网络参数
  Blob<float>* input_layer = net_->input_blobs()[0];
  input_geometry_ = Size(input_layer->width(), input_layer->height());//获取网络规定的输入图像尺寸(宽与高)
  Blob<float>* output_layer = net_->output_blobs()[0];
}

int Classifier::Classify(const Mat& img) {
  num_channels_ = 1;//在这里网络接受单通道的图片
  Blob<float>* input_layer = net_->input_blobs()[0];
  input_layer->Reshape(1, num_channels_,
                       input_geometry_.height, input_geometry_.width);//网络输入blob(1,3,height,width)
  net_->Reshape();//初始化网络各层
  std::vector<Mat> input_channels;
  Init_Img(img, &input_channels);//将输入图片放入网络的输入blob
  net_->Forward();//网络前向传播
  Blob<float>* output_layer = net_->output_blobs()[0];
  const float* begin = output_layer->cpu_data();
  const float* end = begin + output_layer->channels();
  vector<float> percent = vector<float>(begin, end);  //读取测试概率
  for(int i=0;i<percent.size();i++)
    if(percent[i]>0.5)
      return i;       //返回测试结果
}

void Classifier::Init_Img(const Mat& img, std::vector<Mat>* input_channels) {
  Blob<float>* input_layer = net_->input_blobs()[0];
  int width = input_layer->width();//网络规定的输入图像的宽
  int height = input_layer->height();//网络规定的输入图像的高
  float* input_data = input_layer->mutable_cpu_data();
  for (int i = 0; i < input_layer->channels(); ++i) {
    Mat channel(height, width, CV_32FC1, input_data);
    input_channels->push_back(channel);
    input_data += width * height;
  }//这个for循环将网络的输入blob同input_channels关联起来
  Mat sample_float;
  img.convertTo(sample_float, CV_32FC3);
  Mat sample_normalized;
  subtract(sample_float,(33.3184,0,0), sample_normalized);//减去均值，均值可以自己求，也可以通过.binaryproto均值文件求出
  split(sample_normalized, *input_channels);//将输入图片放入input_channels，即放入了网络的输入blob
}

